package cn.tqfeiyang.springplus.framework.security;

import cn.tqfeiyang.springplus.framework.Constants;
import cn.tqfeiyang.springplus.framework.security.loader.InMemoryAuthenticationUserLoader;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingClass;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;

import java.time.Duration;
import java.util.Arrays;
import java.util.Collections;

@Configuration
public class SecurityConfiguration {

    @Resource
    private AuthenticationFilter authenticationFilter;

    @Resource
    private AccessDeniedHandler accessDeniedHandler;

    @Resource
    private AuthenticationEntryPoint authenticationEntryPoint;

    @Bean
    public PasswordEncoder passwordEncoder() {
        BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder();
        System.out.println(bCryptPasswordEncoder.encode("123456"));
        return new BCryptPasswordEncoder();
    }

    @Bean
    public AuthenticationManager authenticationManager(
            AuthenticationConfiguration authenticationConfiguration) throws Exception {
        return authenticationConfiguration.getAuthenticationManager();
    }

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity httpSecurity) throws Exception {
        return httpSecurity
                .securityMatcher( "/api/**", "/app/**")
                .authorizeRequests(authorizeRequests -> authorizeRequests
                        /* 用户登录 不需要授权 */
                        .requestMatchers(Constants.AUTH_LOGIN_URL).permitAll()
                        .requestMatchers(Constants.AUTH_LOGOUT_URL).permitAll()
                        //.requestMatchers(Constants.AUTH_REFRESH_URL).permitAll()
                        /* 应答控制 不需要授权 */
                        .requestMatchers(Constants.ECHO_HELLO_URL).permitAll()
                        /* 错误处理 不需要授权 */
                        .requestMatchers("/error").permitAll()
                        .requestMatchers("/exception").permitAll()
                        /* 其它地址 需要授权 */
                        .anyRequest().authenticated()
                )
                .cors(corsCustomizer->corsCustomizer.configurationSource(new CorsConfigurationSource() {
                    @Override
                    public CorsConfiguration getCorsConfiguration(HttpServletRequest request) {
                        CorsConfiguration corsConfiguration=new CorsConfiguration();
                        //AllowCredentials 和 AllowedOrigins 不要同时启用
                        //corsConfiguration.setAllowCredentials(true);// allows taking authentication with credentials
                        //corsConfiguration.setAllowedOrigins(Arrays.asList("http://localhost:7070")); // providing the allowed origin details, can provide multiple origins here, 7070 is the port number of client application here
                        corsConfiguration.setAllowedOrigins(Arrays.asList("*")); // providing the allowed origin details, can provide multiple origins here, 7070 is the port number of client application here
                        corsConfiguration.setAllowedMethods(Collections.singletonList("*"));// allowing all HTTP methods GET,POST,PUT etc, can configure on your need
                        corsConfiguration.setAllowedHeaders(Collections.singletonList("*"));// allowing all the request headers, can configure according to your need, which headers to allow
                        corsConfiguration.setMaxAge(Duration.ofMinutes(5L)); // setting the max time till which the allowed origin will not make a pre-flight request again to check if the CORS is allowed on not
                        return corsConfiguration;
                    }
                }))
                .sessionManagement(sm -> sm.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
                .exceptionHandling(customizer -> customizer
                        .accessDeniedHandler(accessDeniedHandler)
                        .authenticationEntryPoint(authenticationEntryPoint)
                )
                .addFilterBefore(authenticationFilter, UsernamePasswordAuthenticationFilter.class)
                .formLogin(customizer -> customizer.disable())
                .csrf(csrf -> csrf.disable())
                .build();
    }
}
